/*
 * ProportionalLayout.java 18 sept 08
 *
 * Sweet Home 3D, Copyright (c) 2008 Emmanuel PUYBARET / eTeks <info@eteks.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.eteks.sweethome3d.swing;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;

/**
 * A layout manager that displays two components at the top of each other. 
 * The component at top is sized at container width and at its preferred height.
 * The component at bottom is centered in the rest of the space and sized proportionally 
 * to its preferred size.
 * @author Emmanuel Puybaret
 */
public class ProportionalLayout implements LayoutManager2
{
	/**
	 * The two locations where components managed by a {@link ProportionalLayout} instance can be placed.
	 */
	public enum Constraints
	{
		TOP, BOTTOM
	}
	
	private Component topComponent;
	private Component bottomComponent;
	private int gap;
	
	/**
	 * Creates a layout manager which layouts its component 
	 * with a default gap of 5 pixels between them.
	 */
	public ProportionalLayout()
	{
		this(5);
	}
	
	/**
	 * Creates a layout manager which layouts its component 
	 * with a given <code>gap</code> between them.
	 */
	public ProportionalLayout(int gap)
	{
		this.gap = gap;
	}
	
	/**
	 * Records a given <code>component</code> in this layout manager as the component at 
	 * <code>Constraints.TOP</code> or at <code>Constraints.BOTTOM</code> of its container.
	 */
	public void addLayoutComponent(Component component, Object constraints)
	{
		if (constraints == Constraints.TOP)
		{
			this.topComponent = component;
		}
		else if (constraints == Constraints.BOTTOM)
		{
			this.bottomComponent = component;
		}
	}
	
	/**
	 * Do not use.
	 */
	public void addLayoutComponent(String name, Component comp)
	{
		throw new IllegalArgumentException("Use addLayoutComponent with a Constraints object");
	}
	
	/**
	 * Removes the given <code>component</code> from the ones managed by this layout manager.
	 */
	public void removeLayoutComponent(Component component)
	{}
	
	/**
	 * Returns 0.5.
	 */
	public float getLayoutAlignmentX(Container target)
	{
		return 0.5f;
	}
	
	/**
	 * Return 0.
	 */
	public float getLayoutAlignmentY(Container target)
	{
		return 0f;
	}
	
	/**
	 * Invalidates layout.
	 */
	public void invalidateLayout(Container target)
	{
		// Sizes are computed on the fly each time
	}
	
	/**
	 * Layouts the container.
	 */
	public void layoutContainer(Container parent)
	{
		Insets parentInsets = parent.getInsets();
		int parentAvailableWidth = parent.getWidth() - parentInsets.left - parentInsets.right;
		int parentAvailableHeight = parent.getHeight() - parentInsets.top - parentInsets.bottom;
		
		// Component at top is sized at container width and at its preferred height
		boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
		if (topComponentUsed)
		{
			this.topComponent.setBounds(parentInsets.left, parentInsets.top, parentAvailableWidth,
					Math.min(this.topComponent.getPreferredSize().height, parentAvailableHeight));
		}
		// Component is centered in the rest of the space and sized proportionally to its preferred size
		if (this.bottomComponent != null && this.bottomComponent.getParent() != null)
		{
			Dimension bottomComponentPreferredSize = this.bottomComponent.getPreferredSize();
			int bottomComponentHeight = parentAvailableHeight;
			int bottomComponentY = parentInsets.top;
			if (topComponentUsed)
			{
				int occupiedHeight = this.topComponent.getHeight() + this.gap;
				bottomComponentHeight -= occupiedHeight;
				bottomComponentY += occupiedHeight;
			}
			int bottomComponentWidth = bottomComponentHeight * bottomComponentPreferredSize.width
					/ bottomComponentPreferredSize.height;
			int bottomComponentX = parentInsets.left;
			// Adjust component width and height if it's larger than parent
			if (bottomComponentWidth > parentAvailableWidth)
			{
				bottomComponentWidth = parentAvailableWidth;
				int previousHeight = bottomComponentHeight;
				bottomComponentHeight = bottomComponentWidth * bottomComponentPreferredSize.height
						/ bottomComponentPreferredSize.width;
				bottomComponentY += (previousHeight - bottomComponentHeight) / 2;
			}
			else
			{
				// Center component in width
				bottomComponentX += (parentAvailableWidth - bottomComponentWidth) / 2;
			}
			
			this.bottomComponent.setBounds(bottomComponentX, bottomComponentY, bottomComponentWidth,
					bottomComponentHeight);
		}
	}
	
	/**
	 * Returns the largest minimum width of the components managed by this layout manager,
	 * and the sum of their minimum heights.
	 */
	public Dimension minimumLayoutSize(Container parent)
	{
		Insets parentInsets = parent.getInsets();
		int minWidth = 0;
		int minHeight = 0;
		boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
		if (topComponentUsed)
		{
			Dimension topComponentMinSize = this.topComponent.getMinimumSize();
			minWidth = Math.max(minWidth, topComponentMinSize.width);
			minHeight = topComponentMinSize.height;
		}
		if (this.bottomComponent != null && this.bottomComponent.getParent() != null)
		{
			Dimension bottomComponentMinSize = this.bottomComponent.getMinimumSize();
			minWidth = Math.max(minWidth, bottomComponentMinSize.width);
			minHeight += bottomComponentMinSize.height;
			if (topComponentUsed)
			{
				minHeight += this.gap;
			}
		}
		
		return new Dimension(minWidth + parentInsets.left + parentInsets.right,
				minHeight + parentInsets.top + parentInsets.bottom);
	}
	
	/**
	 * Returns the largest maximum width of the components managed by this layout manager,
	 * and the sum of their maximum heights.
	 */
	public Dimension maximumLayoutSize(Container parent)
	{
		Insets parentInsets = parent.getInsets();
		int maxWidth = 0;
		int maxHeight = 0;
		boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
		if (topComponentUsed)
		{
			Dimension topComponentMaxSize = this.topComponent.getMaximumSize();
			maxWidth = Math.max(maxWidth, topComponentMaxSize.width);
			maxHeight = topComponentMaxSize.height;
		}
		if (this.bottomComponent != null && this.bottomComponent.getParent() != null)
		{
			Dimension bottomComponentMaxSize = this.bottomComponent.getMaximumSize();
			maxWidth = Math.max(maxWidth, bottomComponentMaxSize.width);
			maxHeight += bottomComponentMaxSize.height;
			if (topComponentUsed)
			{
				maxHeight += this.gap;
			}
		}
		
		return new Dimension(maxWidth + parentInsets.left + parentInsets.right,
				maxHeight + parentInsets.top + parentInsets.bottom);
	}
	
	/**
	 * Returns the largest preferred width of the components managed by this layout manager,
	 * and the sum of their preferred heights.
	 */
	public Dimension preferredLayoutSize(Container parent)
	{
		Insets parentInsets = parent.getInsets();
		int preferredWidth = 0;
		int preferredHeight = 0;
		boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
		if (topComponentUsed)
		{
			Dimension topComponentPreferredSize = this.topComponent.getPreferredSize();
			preferredWidth = Math.max(preferredWidth, topComponentPreferredSize.width);
			preferredHeight = topComponentPreferredSize.height;
		}
		if (this.bottomComponent != null && this.bottomComponent.getParent() != null)
		{
			Dimension bottomComponentPreferredSize = this.bottomComponent.getPreferredSize();
			preferredWidth = Math.max(preferredWidth, bottomComponentPreferredSize.width);
			preferredHeight += bottomComponentPreferredSize.height;
			if (topComponentUsed)
			{
				preferredHeight += this.gap;
			}
		}
		
		return new Dimension(preferredWidth + parentInsets.left + parentInsets.right,
				preferredHeight + parentInsets.top + parentInsets.bottom);
	}
}
